/**
 * 
 */
package com.yv.security.core.validate.code.sms;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import com.yv.security.core.properties.SecurityProperties;
import com.yv.security.core.validate.code.ValidateCode;
import com.yv.security.core.validate.code.ValidateCodeException;
import com.yv.security.core.validate.code.ValidateCodeProcessor;
import com.yv.security.core.validate.code.image.ImageCode;

/**
 * 验证码校验Basic version
 * 仅适用于短信验证码，(旧的validateCodeFilter只适用于图形验证码)。重构之后的版本ValidateCodeFilter短信和图形都适用。
 * 并没有使用ValidateCodeProcessor
 * 没有Component注解是因为是在BrowserSecurityConfig里直接new的
 * @author 
 *
 */

public class SmsCodeFilter extends OncePerRequestFilter implements InitializingBean {
	
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
	
	// 验证码校验失败处理器,如果在本类里没有new,也没有autowired,则是在调用该filter处设置的.
	private AuthenticationFailureHandler authenticationFailureHandler;
	
	// 图片验证码拦截的路径'
	private Set<String> urls =  new HashSet<>();
	
	private SecurityProperties securityProperties;
	
	// 对于包含通配符的路径,用AntPatchMatcher匹配
	private AntPathMatcher antPathMatcher = new AntPathMatcher();
	

	// version2  可配置拦截的路径,实现InitializingBean,重写该方法,初始化需要拦截的路径
	@Override
	public void afterPropertiesSet() throws ServletException {
		super.afterPropertiesSet();
		String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getSms().getUrl(), ",");

		if(ArrayUtils.isNotEmpty(configUrls)) {
			for (String string : configUrls) {
				urls.add(string);
			}
		}
		urls.add("/authentication/mobile");
		logger.info("-----[SmsCodeFilter]  afterPropertiesSet urls: "+urls.toString());
	}
	
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		logger.info("-----[SmsCodeFilter] doFilterInternal was called, request URI: "+request.getRequestURI());
		boolean action = false;
		for (String string : urls) {
			if(antPathMatcher.match(string, request.getRequestURI())) {
				action = true;
			}
		}
		if(action){
			try {
				validate(new ServletWebRequest(request));
			} catch(ValidateCodeException exception) {
				authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
				return;
			}
		}
		
		// If it's not a login request, then pass.
		chain.doFilter(request, response);

	}
	
	private void validate(ServletWebRequest request) {
		
		logger.info("[ValidateCodeFilter] validate().....");
		ValidateCode codeInSession = (ValidateCode) sessionStrategy.getAttribute(request, ValidateCodeProcessor.SESSION_KEY_PREFIX+"SMS");

		String codeInRequest;
		try {
			// The second refers to the parameter name from frontpage request
			codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"smsCode");
		} catch (ServletRequestBindingException e) {
			throw new ValidateCodeException("获取验证码的值失败");
		}

		if (StringUtils.isBlank(codeInRequest)) {
			throw new ValidateCodeException( "验证码的值不能为空");
		}

		if (codeInSession == null) {
			throw new ValidateCodeException("验证码不存在");
		}

		if (codeInSession.isExpried()) {
			sessionStrategy.removeAttribute(request, ValidateCodeProcessor.SESSION_KEY_PREFIX+"SMS");
			throw new ValidateCodeException("验证码已过期");
		}

		if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
			throw new ValidateCodeException("验证码不匹配");
		}

		sessionStrategy.removeAttribute(request, ValidateCodeProcessor.SESSION_KEY_PREFIX+"SMS");
		
	}

	public AuthenticationFailureHandler getAuthenticationFailureHandler() {
		return authenticationFailureHandler;
	}

	public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
		this.authenticationFailureHandler = authenticationFailureHandler;
	}

	public SecurityProperties getSecurityProperties() {
		return securityProperties;
	}

	public void setSecurityProperties(SecurityProperties securityProperties) {
		this.securityProperties = securityProperties;
	}

	

}
